package com.bkhech.netty.websocket;

import cn.hutool.core.util.StrUtil;
import io.netty.channel.Channel;
import io.netty.channel.group.ChannelGroup;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import com.bkhech.pojo.netty.DataContent;
import com.bkhech.utils.JsonUtils;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * UserChannelSession
 *
 * 用户会话管理，用户id和Channel的关联处理
 *
 * @author guowm
 * @date 2024-11-11
 */
public class UserChannelSession {
    // 用于多端同时接收消息，允许一个账号多个设备同时在线，比如 ipad，ios，Mac等设置同时收到消息
    // key:userId  value: 用户的多个channel
    private static final Map<String, List<Channel>> multiSession = new HashMap<>(32);

    // 用于记录用户ID和客户端Channel长id的关联关系
    // key:客户端长id  value: userId
    private static final Map<String, String> userChannelIdMap = new HashMap<>(32);

    public static void putChannelIdOfUser(String channelId, String userId) {
        userChannelIdMap.put(channelId, userId);
    }

    public static String getUserIdByChannelId(String channelId) {
        return userChannelIdMap.get(channelId);
    }

    /**
     * 设置用户关联的连接
     * @param userId
     * @param channel
     */
    public static void putMultiChannels(String userId, Channel channel) {
        List<Channel> channelList = getMultiChannels(userId);
        if (channelList == null) {
            channelList = new ArrayList<>();
        }
        channelList.add(channel);
        multiSession.put(userId, channelList);
    }

    /**
     * 移除无效的连接
     * @param userId
     * @param channelId Channel 长 Id
     */
    public static void removeUselessChannels(String userId, String channelId) {
        List<Channel> channelList = getMultiChannels(userId);
        if (CollectionUtils.isEmpty(channelList)) {
            return;
        }

        channelList.removeIf(c -> Objects.equals(channelId, c.id().asLongText()));
        // 重新覆盖
        multiSession.put(userId, channelList);
    }

    /**
     * 获取用户所有关联的连接（多端设备）
     * @param userId
     * @return
     */
    public static List<Channel> getMultiChannels(String userId) {
        return multiSession.get(userId);
    }

    /**
     * 获取用户除 excludeChannelId 之外的所有其他连接
     * @param userId
     * @param excludeChannelId 排除的 channelId
     * @return
     */
    public static List<Channel> getMyOtherChannel(String userId, String excludeChannelId) {
        List<Channel> multiChannels = getMultiChannels(userId);
        if (CollectionUtils.isEmpty(multiChannels)) {
            return new ArrayList<>();
        }

        return multiChannels.stream().filter(c -> !StrUtil.isBlank(excludeChannelId) && !excludeChannelId.equals(c.id().asLongText()))
                .collect(Collectors.toList());
    }

    /**
     * 发送消息
     *  场景1. 同账户（消息接受者）多端设备消息发送
     *  场景2. 同账户（消息接受者）除了当前设备外，将消息发送给其他设备
     * @param receiverChannels
     * @param dataContent
     */
    public static void sendToTarget(List<Channel> receiverChannels, DataContent dataContent) {
        final ChannelGroup channelGroup = ClientChannelGroup.getInstance();

        if (receiverChannels == null) {
            return;
        }

        for (Channel c : receiverChannels) {
            // 是否在当前服务中，在就发送（集群化部署时必需）
            Channel findChannel = channelGroup.find(c.id());
            if (findChannel != null) {
                findChannel.writeAndFlush(
                        new TextWebSocketFrame(JsonUtils.objectToJson(dataContent))
                );
            }

        }
    }
}
